<!doctype html>



  


<html class="theme-next muse use-motion" lang="zh-Hans">
<head>
  <meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>



<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />












  
  
  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />




  
  
  
  

  
    
    
  

  

  

  

  

  
    
    
    <link href="//fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
  






<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />

<link href="/css/main.css?v=5.1.0" rel="stylesheet" type="text/css" />


  <meta name="keywords" content="Python," />








  <link rel="shortcut icon" type="image/x-icon" href="/favicon.ico?v=5.1.0" />






<meta name="description" content="这是一篇在Stack overflow上很热的帖子。提问者自称已经掌握了有关Python OOP编程中的各种概念，但始终觉得元类(metaclass)难以理解。他知道这肯定和自省有关，但仍然觉得不太明白，希望大家可以给出一些实际的例子和代码片段以帮助理解，以及在什么情况下需要进行元编程。于是e-satis同学给出了神一般的回复，该回复获得了985点的赞同点数，更有人评论说这段回复应该加入到Pyth">
<meta property="og:type" content="article">
<meta property="og:title" content="深刻理解Python中的元类（metaclass）">
<meta property="og:url" content="/2017/02/06/深刻理解Python中的元类（metaclass）/index.html">
<meta property="og:site_name" content="Porson's Bolg">
<meta property="og:description" content="这是一篇在Stack overflow上很热的帖子。提问者自称已经掌握了有关Python OOP编程中的各种概念，但始终觉得元类(metaclass)难以理解。他知道这肯定和自省有关，但仍然觉得不太明白，希望大家可以给出一些实际的例子和代码片段以帮助理解，以及在什么情况下需要进行元编程。于是e-satis同学给出了神一般的回复，该回复获得了985点的赞同点数，更有人评论说这段回复应该加入到Pyth">
<meta property="og:updated_time" content="2017-02-23T03:31:11.750Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="深刻理解Python中的元类（metaclass）">
<meta name="twitter:description" content="这是一篇在Stack overflow上很热的帖子。提问者自称已经掌握了有关Python OOP编程中的各种概念，但始终觉得元类(metaclass)难以理解。他知道这肯定和自省有关，但仍然觉得不太明白，希望大家可以给出一些实际的例子和代码片段以帮助理解，以及在什么情况下需要进行元编程。于是e-satis同学给出了神一般的回复，该回复获得了985点的赞同点数，更有人评论说这段回复应该加入到Pyth">



<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Muse',
    sidebar: {"position":"right","display":"post"},
    fancybox: true,
    motion: true,
    duoshuo: {
      userId: 'porson',
      author: '博主'
    },
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>



  <link rel="canonical" href="/2017/02/06/深刻理解Python中的元类（metaclass）/"/>





  <title> 深刻理解Python中的元类（metaclass） | Porson's Bolg </title>
</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  










  
  
    
  

  <div class="container one-collumn sidebar-position-right page-post-detail ">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-meta custom-logo">
  
    <div class="site-meta-headline">
      <a>
        <img class="custom-logo-image" src="/images/favicon.png"
             alt="Porson's Bolg"/>
      </a>
    </div>
  

  <div class="custom-logo-site-title">
    <a href="/"  class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <span class="site-title">Porson's Bolg</span>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>
    
      <p class="site-subtitle">MySQL/Python/Golang/Others</p>
    
</div>

<div class="site-nav-toggle">
  <button>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
    <span class="btn-bar"></span>
  </button>
</div>

<nav class="site-nav">
  

  
    <ul id="menu" class="menu">
      
        
        <li class="menu-item menu-item-home">
          <a href="/" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
            
            首页
          </a>
        </li>
      
        
        <li class="menu-item menu-item-about">
          <a href="/about" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-user-md"></i> <br />
            
            关于
          </a>
        </li>
      
        
        <li class="menu-item menu-item-archives">
          <a href="/archives" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
            
            归档
          </a>
        </li>
      
        
        <li class="menu-item menu-item-tags">
          <a href="/tags" rel="section">
            
              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
            
            标签
          </a>
        </li>
      

      
    </ul>
  

  
</nav>



 </div>
    </header>

    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  <article class="post post-type-normal " itemscope itemtype="http://schema.org/Article">
  <link itemprop="mainEntityOfPage" href="/2017/02/06/深刻理解Python中的元类（metaclass）/">

  <span style="display:none" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <meta itemprop="name" content="Porson">
    <meta itemprop="description" content="">
    <meta itemprop="image" content="/images/avatar.gif">
  </span>

  <span style="display:none" itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
    <meta itemprop="name" content="Porson's Bolg">
    <span style="display:none" itemprop="logo" itemscope itemtype="http://schema.org/ImageObject">
      <img style="display:none;" itemprop="url image" alt="Porson's Bolg" src="/images/favicon.png">
    </span>
  </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">
            
            
              
                深刻理解Python中的元类（metaclass）
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">
            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              
              <time title="Post created" itemprop="dateCreated datePublished" datetime="2017-02-06T11:20:36+08:00">
                2017-02-06
              </time>
            

            

            
          </span>

          

          
            
              <span class="post-comments-count">
                <span class="post-meta-divider">|</span>
                <span class="post-meta-item-icon">
                  <i class="fa fa-comment-o"></i>
                </span>
                <a href="/2017/02/06/深刻理解Python中的元类（metaclass）/#comments" itemprop="discussionUrl">
                  <span class="post-comments-count ds-thread-count" data-thread-key="2017/02/06/深刻理解Python中的元类（metaclass）/" itemprop="commentCount"></span>
                </a>
              </span>
            
          

          

          
          

          

          

        </div>
      </header>
    


    <div class="post-body" itemprop="articleBody">

      
      

      
        <p>这是一篇在<a href="http://stackoverflow.com/questions/100003/what-is-a-metaclass-in-python" target="_blank" rel="external">Stack overflow</a>上很热的帖子。提问者自称已经掌握了有关Python OOP编程中的各种概念，但始终觉得元类(metaclass)难以理解。他知道这肯定和自省有关，但仍然觉得不太明白，希望大家可以给出一些实际的例子和代码片段以帮助理解，以及在什么情况下需要进行元编程。于是e-satis同学给出了神一般的回复，该回复获得了985点的赞同点数，更有人评论说这段回复应该加入到Python的官方文档中去。而e-satis同学本人在Stack Overflow中的声望积分也高达64271分。以下就是这篇精彩的回复。（提示：非常长）</p>
<a id="more"></a>
<h2 id="类也是对象"><a href="#类也是对象" class="headerlink" title="类也是对象"></a><strong>类也是对象</strong></h2><p>在理解元类之前，你需要先掌握Python中的类。Python中类的概念借鉴于Smalltalk，这显得有些奇特。在大多数编程语言中，类就是一组用来描述如何生成一个对象的代码段。在Python中这一点仍然成立：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="class"><span class="keyword">class</span> <span class="title">ObjectCreator</span><span class="params">(object)</span>:</span></div><div class="line">…       <span class="keyword">pass</span></div><div class="line">…</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>my_object = ObjectCreator()</div><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">print</span> my_object</div><div class="line">&lt;__main__.ObjectCreator object at <span class="number">0x8974f2c</span>&gt;</div></pre></td></tr></table></figure>
<p>但是，Python中的类还远不止如此。类同样也是一种对象。是的，没错，就是对象。只要你使用关键字class，Python解释器在执行的时候就会创建一个对象。下面的代码段：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="class"><span class="keyword">class</span> <span class="title">ObjectCreator</span><span class="params">(object)</span>:</span></div><div class="line">…       <span class="keyword">pass</span></div><div class="line">…</div></pre></td></tr></table></figure>
<p>将在内存中创建一个对象，名字就是ObjectCreator。<strong>这个对象（类）自身拥有创建对象（类实例）的能力，而这就是为什么它是一个类的原因。</strong>但是，它的本质仍然是一个对象，于是乎你可以对它做如下的操作：</p>
<ol>
<li>你可以将它赋值给一个变量</li>
<li>你可以拷贝它</li>
<li>你可以为它增加属性</li>
<li>你可以将它作为函数参数进行传递</li>
</ol>
<p>下面是示例：</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line">&gt;&gt;&gt; print ObjectCreator     # 你可以打印一个类，因为它其实也是一个对象</div><div class="line">&lt;class '__main__.ObjectCreator'&gt;</div><div class="line">&gt;&gt;&gt; def echo(o):</div><div class="line">…       print o</div><div class="line">…</div><div class="line">&gt;&gt;&gt; echo(ObjectCreator)                 # 你可以将类做为参数传给函数</div><div class="line">&lt;class '__main__.ObjectCreator'&gt;</div><div class="line">&gt;&gt;&gt; print hasattr(ObjectCreator, 'new_attribute')</div><div class="line">Fasle</div><div class="line">&gt;&gt;&gt; ObjectCreator.new_attribute = 'foo' #  你可以为类增加属性</div><div class="line">&gt;&gt;&gt; print hasattr(ObjectCreator, 'new_attribute')</div><div class="line">True</div><div class="line">&gt;&gt;&gt; print ObjectCreator.new_attribute</div><div class="line">foo</div><div class="line">&gt;&gt;&gt; ObjectCreatorMirror = ObjectCreator # 你可以将类赋值给一个变量</div><div class="line">&gt;&gt;&gt; print ObjectCreatorMirror()</div><div class="line">&lt;__main__.ObjectCreator object at 0x8997b4c&gt;</div></pre></td></tr></table></figure>
<h2 id="动态地创建类"><a href="#动态地创建类" class="headerlink" title="动态地创建类"></a><strong>动态地创建类</strong></h2><p>因为类也是对象，你可以在运行时动态的创建它们，就像其他任何对象一样。首先，你可以在函数中创建类，使用class关键字即可。</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">&gt;&gt;&gt; def choose_class(name):</div><div class="line">…       if name == 'foo':</div><div class="line">…           class Foo(object):</div><div class="line">…               pass</div><div class="line">…           return Foo     # 返回的是类，不是类的实例</div><div class="line">…       else:</div><div class="line">…           class Bar(object):</div><div class="line">…               pass</div><div class="line">…           return Bar</div><div class="line">…</div><div class="line">&gt;&gt;&gt; MyClass = choose_class('foo')</div><div class="line">&gt;&gt;&gt; print MyClass              # 函数返回的是类，不是类的实例</div><div class="line">&lt;class '__main__'.Foo&gt;</div><div class="line">&gt;&gt;&gt; print MyClass()            # 你可以通过这个类创建类实例，也就是对象</div><div class="line">&lt;__main__.Foo object at 0x89c6d4c&gt;</div></pre></td></tr></table></figure>
<p>但这还不够动态，因为你仍然需要自己编写整个类的代码。由于类也是对象，所以它们必须是通过什么东西来生成的才对。当你使用class关键字时，Python解释器自动创建这个对象。但就和Python中的大多数事情一样，Python仍然提供给你手动处理的方法。还记得内建函数type吗？这个古老但强大的函数能够让你知道一个对象的类型是什么，就像这样：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">print</span> type(<span class="number">1</span>)</div><div class="line">&lt;type <span class="string">'int'</span>&gt;</div><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">print</span> type(<span class="string">"1"</span>)</div><div class="line">&lt;type <span class="string">'str'</span>&gt;</div><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">print</span> type(ObjectCreator)</div><div class="line">&lt;type <span class="string">'type'</span>&gt;</div><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="keyword">print</span> type(ObjectCreator())</div><div class="line">&lt;<span class="class"><span class="keyword">class</span> '<span class="title">__main__</span>.<span class="title">ObjectCreator</span>'&gt;</span></div></pre></td></tr></table></figure>
<p>这里，type有一种完全不同的能力，它也能动态的创建类。type可以接受一个类的描述作为参数，然后返回一个类。（我知道，根据传入参数的不同，同一个函数拥有两种完全不同的用法是一件很傻的事情，但这在Python中是为了保持向后兼容性）</p>
<p>type可以像这样工作：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">type(类名, 父类的元组（针对继承的情况，可以为空），包含属性的字典（名称和值）)</div></pre></td></tr></table></figure>
<p>比如下面的代码：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"></div><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="class"><span class="keyword">class</span> <span class="title">MyShinyClass</span><span class="params">(object)</span>:</span></div><div class="line">…       <span class="keyword">pass</span></div></pre></td></tr></table></figure>
<p>可以手动像这样创建：</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&gt;&gt;&gt; MyShinyClass = type('MyShinyClass', (), &#123;&#125;)  # 返回一个类对象</div><div class="line">&gt;&gt;&gt; print MyShinyClass</div><div class="line">&lt;class '__main__.MyShinyClass'&gt;</div><div class="line">&gt;&gt;&gt; print MyShinyClass()  #  创建一个该类的实例</div><div class="line">&lt;__main__.MyShinyClass object at 0x8997cec&gt;</div></pre></td></tr></table></figure>
<p>你会发现我们使用“MyShinyClass”作为类名，并且也可以把它当做一个变量来作为类的引用。类和变量是不同的，这里没有任何理由把事情弄的复杂。</p>
<p>type 接受一个字典来为类定义属性，因此</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="class"><span class="keyword">class</span> <span class="title">Foo</span><span class="params">(object)</span>:</span></div><div class="line">…       bar = <span class="keyword">True</span></div></pre></td></tr></table></figure>
<p>可以翻译为：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span>Foo = type(<span class="string">'Foo'</span>, (), &#123;<span class="string">'bar'</span>:<span class="keyword">True</span>&#125;)</div></pre></td></tr></table></figure>
<p>并且可以将Foo当成一个普通的类一样使用：</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">&gt;&gt;&gt; print Foo</div><div class="line">&lt;class '__main__.Foo'&gt;</div><div class="line">&gt;&gt;&gt; print Foo.bar</div><div class="line">True</div><div class="line">&gt;&gt;&gt; f = Foo()</div><div class="line">&gt;&gt;&gt; print f</div><div class="line">&lt;__main__.Foo object at 0x8a9b84c&gt;</div><div class="line">&gt;&gt;&gt; print f.bar</div><div class="line">True</div></pre></td></tr></table></figure>
<p>当然，你可以向这个类继承，所以，如下的代码：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="class"><span class="keyword">class</span> <span class="title">FooChild</span><span class="params">(Foo)</span>:</span></div><div class="line">…       <span class="keyword">pass</span></div></pre></td></tr></table></figure>
<p>就可以写成：</p>
<figure class="highlight"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">&gt;&gt;&gt; FooChild = type('FooChild', (Foo,),&#123;&#125;)</div><div class="line">&gt;&gt;&gt; print FooChild</div><div class="line">&lt;class '__main__.FooChild'&gt;</div><div class="line">&gt;&gt;&gt; print FooChild.bar   # bar属性是由Foo继承而来</div><div class="line">True</div></pre></td></tr></table></figure>
<p>最终你会希望为你的类增加方法。只需要定义一个有着恰当签名的函数并将其作为属性赋值就可以了。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="function"><span class="keyword">def</span> <span class="title">echo_bar</span><span class="params">(self)</span>:</span></div><div class="line">…       <span class="keyword">print</span> self.bar</div><div class="line">…</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>FooChild = type(<span class="string">'FooChild'</span>, (Foo,), &#123;<span class="string">'echo_bar'</span>: echo_bar&#125;)</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>hasattr(Foo, <span class="string">'echo_bar'</span>)</div><div class="line"><span class="keyword">False</span></div><div class="line"><span class="meta">&gt;&gt;&gt; </span>hasattr(FooChild, <span class="string">'echo_bar'</span>)</div><div class="line"><span class="keyword">True</span></div><div class="line"><span class="meta">&gt;&gt;&gt; </span>my_foo = FooChild()</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>my_foo.echo_bar()</div><div class="line"><span class="keyword">True</span></div></pre></td></tr></table></figure>
<p>你可以看到，在Python中，类也是对象，你可以动态的创建类。这就是当你使用关键字class时Python在幕后做的事情，而这就是通过元类来实现的。</p>
<h2 id="到底什么是元类（终于到主题了）"><a href="#到底什么是元类（终于到主题了）" class="headerlink" title="到底什么是元类（终于到主题了）"></a><strong>到底什么是元类（终于到主题了）</strong></h2><p>元类就是用来创建类的“东西”。你创建类就是为了创建类的实例对象，不是吗？但是我们已经学习到了Python中的类也是对象。好吧，元类就是用来创建这些类（对象）的，元类就是类的类，你可以这样理解 为：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">MyClass = MetaClass()</div><div class="line">MyObject = MyClass()</div></pre></td></tr></table></figure>
<p>你已经看到了type可以让你像这样做：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">MyClass = type(<span class="string">'MyClass'</span>, (), &#123;&#125;)</div></pre></td></tr></table></figure>
<p>这是因为函数type实际上是一个元类。type就是Python在背后用来创建所有类的元类。现在你想知道那为什么type会全部采用小写形式而不是Type呢？好吧，我猜这是为了和str保持一致性，str是用来创建字符串对象的类，而int是用来创建整数对象的类。type就是创建类对象的类。你可以通过检查<strong>class</strong>属性来看到这一点。Python中所有的东西，注意，我是指所有的东西——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象，而且它们都是从一个类创建而来。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span>age = <span class="number">35</span></div><div class="line"><span class="meta">&gt;&gt;&gt; </span>age.__class__</div><div class="line">&lt;type <span class="string">'int'</span>&gt;</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>name = <span class="string">'bob'</span></div><div class="line"><span class="meta">&gt;&gt;&gt; </span>name.__class__</div><div class="line">&lt;type <span class="string">'str'</span>&gt;</div><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="function"><span class="keyword">def</span> <span class="title">foo</span><span class="params">()</span>:</span> <span class="keyword">pass</span></div><div class="line">&gt;&gt;&gt;foo.__class__</div><div class="line">&lt;type <span class="string">'function'</span>&gt;</div><div class="line"><span class="meta">&gt;&gt;&gt; </span><span class="class"><span class="keyword">class</span> <span class="title">Bar</span><span class="params">(object)</span>:</span> <span class="keyword">pass</span></div><div class="line"><span class="meta">&gt;&gt;&gt; </span>b = Bar()</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>b.__class__</div><div class="line">&lt;<span class="class"><span class="keyword">class</span> '<span class="title">__main__</span>.<span class="title">Bar</span>'&gt;</span></div></pre></td></tr></table></figure>
<p>现在，对于任何一个<strong>class</strong>的<strong>class</strong>属性又是什么呢？</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="meta">&gt;&gt;&gt; </span>a.__class__.__class__</div><div class="line">&lt;type <span class="string">'type'</span>&gt;</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>age.__class__.__class__</div><div class="line">&lt;type <span class="string">'type'</span>&gt;</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>foo.__class__.__class__</div><div class="line">&lt;type <span class="string">'type'</span>&gt;</div><div class="line"><span class="meta">&gt;&gt;&gt; </span>b.__class__.__class__</div><div class="line">&lt;type <span class="string">'type'</span>&gt;</div></pre></td></tr></table></figure>
<p>因此，元类就是创建类这种对象的东西。如果你喜欢的话，可以把元类称为“类工厂”（不要和工厂类搞混了:D） type就是Python的内建元类，当然了，你也可以创建自己的元类。</p>
<h2 id="metaclass-属性"><a href="#metaclass-属性" class="headerlink" title="__metaclass__**属性**"></a><strong>__metaclass__**</strong>属性**</h2><p>你可以在写一个类的时候为其添加__metaclass__属性。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Foo</span><span class="params">(object)</span>:</span></div><div class="line">	__metaclass__ = something…</div><div class="line">[…]</div></pre></td></tr></table></figure>
<p>如果你这么做了，Python就会用元类来创建类Foo。小心点，这里面有些技巧。你首先写下class Foo(object)，但是类对象Foo还没有在内存中创建。Python会在类的定义中寻找<strong>metaclass</strong>属性，如果找到了，Python就会用它来创建类Foo，如果没有找到，就会用内建的type来创建这个类。把下面这段话反复读几次。当你写如下代码时 :</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Foo</span><span class="params">(Bar)</span>:</span></div><div class="line">    <span class="keyword">pass</span></div></pre></td></tr></table></figure>
<p>Python做了如下的操作：</p>
<p>Foo中有<strong>metaclass</strong>这个属性吗？如果是，Python会在内存中通过<strong>metaclass</strong>创建一个名字为Foo的类对象（我说的是类对象，请紧跟我的思路）。如果Python没有找到<strong>metaclass</strong>，它会继续在Bar（父类）中寻找<strong>metaclass</strong>属性，并尝试做和前面同样的操作。如果Python在任何父类中都找不到<strong>metaclass</strong>，它就会在模块层次中去寻找<strong>metaclass</strong>，并尝试做同样的操作。如果还是找不到<strong>metaclass</strong>,Python就会用内置的type来创建这个类对象。</p>
<p>现在的问题就是，你可以在<strong>metaclass</strong>中放置些什么代码呢？答案就是：可以创建一个类的东西。那么什么可以用来创建一个类呢？type，或者任何使用到type或者子类化type的东东都可以。</p>
<h2 id="自定义元类"><a href="#自定义元类" class="headerlink" title="自定义元类"></a><strong>自定义元类</strong></h2><p>元类的主要目的就是为了当创建类时能够自动地改变类。通常，你会为API做这样的事情，你希望可以创建符合当前上下文的类。假想一个很傻的例子，你决定在你的模块里所有的类的属性都应该是大写形式。有好几种方法可以办到，但其中一种就是通过在模块级别设定<strong>metaclass</strong>。采用这种方法，这个模块中的所有类都会通过这个元类来创建，我们只需要告诉元类把所有的属性都改成大写形式就万事大吉了。</p>
<p>幸运的是，<strong>metaclass</strong>实际上可以被任意调用，它并不需要是一个正式的类（我知道，某些名字里带有‘class’的东西并不需要是一个class，画画图理解下，这很有帮助）。所以，我们这里就先以一个简单的函数作为例子开始。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line"><span class="comment"># 元类会自动将你通常传给‘type’的参数作为自己的参数传入</span></div><div class="line"><span class="function"><span class="keyword">def</span> <span class="title">upper_attr</span><span class="params">(future_class_name, future_class_parents, future_class_attr)</span>:</span></div><div class="line">    <span class="string">'''返回一个类对象，将属性都转为大写形式'''</span></div><div class="line">    <span class="comment">#  选择所有不以'__'开头的属性</span></div><div class="line">    attrs = ((name, value) <span class="keyword">for</span> name, value <span class="keyword">in</span> future_class_attr.items() <span class="keyword">if</span> <span class="keyword">not</span> name.startswith(<span class="string">'__'</span>))</div><div class="line">    <span class="comment"># 将它们转为大写形式</span></div><div class="line">    uppercase_attr = dict((name.upper(), value) <span class="keyword">for</span> name, value <span class="keyword">in</span> attrs)</div><div class="line"> </div><div class="line">    <span class="comment"># 通过'type'来做类对象的创建</span></div><div class="line">    <span class="keyword">return</span> type(future_class_name, future_class_parents, uppercase_attr)</div><div class="line"> </div><div class="line">__metaclass__ = upper_attr  <span class="comment">#  这会作用到这个模块中的所有类</span></div><div class="line"> </div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Foo</span><span class="params">(object)</span>:</span></div><div class="line">    <span class="comment"># 我们也可以只在这里定义__metaclass__，这样就只会作用于这个类中</span></div><div class="line">    bar = <span class="string">'bip'</span></div><div class="line"></div><div class="line"><span class="keyword">print</span> hasattr(Foo, <span class="string">'bar'</span>)</div><div class="line"><span class="comment"># 输出: False</span></div><div class="line"><span class="keyword">print</span> hasattr(Foo, <span class="string">'BAR'</span>)</div><div class="line"><span class="comment"># 输出:True</span></div><div class="line"> </div><div class="line">f = Foo()</div><div class="line"><span class="keyword">print</span> f.BAR</div><div class="line"><span class="comment"># 输出:'bip'</span></div></pre></td></tr></table></figure>
<p>现在让我们再做一次，这一次用一个真正的class来当做元类。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"><span class="comment"># 请记住，'type'实际上是一个类，就像'str'和'int'一样</span></div><div class="line"><span class="comment"># 所以，你可以从type继承</span></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">UpperAttrMetaClass</span><span class="params">(type)</span>:</span></div><div class="line">    <span class="comment"># __new__ 是在__init__之前被调用的特殊方法</span></div><div class="line">    <span class="comment"># __new__是用来创建对象并返回之的方法</span></div><div class="line">    <span class="comment"># 而__init__只是用来将传入的参数初始化给对象</span></div><div class="line">    <span class="comment"># 你很少用到__new__，除非你希望能够控制对象的创建</span></div><div class="line">    <span class="comment"># 这里，创建的对象是类，我们希望能够自定义它，所以我们这里改写__new__</span></div><div class="line">    <span class="comment"># 如果你希望的话，你也可以在__init__中做些事情</span></div><div class="line">    <span class="comment"># 还有一些高级的用法会涉及到改写__call__特殊方法，但是我们这里不用</span></div><div class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__new__</span><span class="params">(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr)</span>:</span></div><div class="line">        attrs = ((name, value) <span class="keyword">for</span> name, value <span class="keyword">in</span> future_class_attr.items() <span class="keyword">if</span> <span class="keyword">not</span> name.startswith(<span class="string">'__'</span>))</div><div class="line">        uppercase_attr = dict((name.upper(), value) <span class="keyword">for</span> name, value <span class="keyword">in</span> attrs)</div><div class="line">        <span class="keyword">return</span> type(future_class_name, future_class_parents, uppercase_attr)</div></pre></td></tr></table></figure>
<p>但是，这种方式其实不是OOP。我们直接调用了type，而且我们没有改写父类的<strong>new</strong>方法。现在让我们这样去处理:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">UpperAttrMetaclass</span><span class="params">(type)</span>:</span></div><div class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__new__</span><span class="params">(upperattr_metaclass, future_class_name, future_class_parents, future_class_attr)</span>:</span></div><div class="line">        attrs = ((name, value) <span class="keyword">for</span> name, value <span class="keyword">in</span> future_class_attr.items() <span class="keyword">if</span> <span class="keyword">not</span> name.startswith(<span class="string">'__'</span>))</div><div class="line">        uppercase_attr = dict((name.upper(), value) <span class="keyword">for</span> name, value <span class="keyword">in</span> attrs)</div><div class="line"> </div><div class="line">        <span class="comment"># 复用type.__new__方法</span></div><div class="line">        <span class="comment"># 这就是基本的OOP编程，没什么魔法</span></div><div class="line">        <span class="keyword">return</span> type.__new__(upperattr_metaclass, future_class_name, future_class_parents, uppercase_attr)</div></pre></td></tr></table></figure>
<p>你可能已经注意到了有个额外的参数upperattr_metaclass，这并没有什么特别的。类方法的第一个参数总是表示当前的实例，就像在普通的类方法中的self参数一样。当然了，为了清晰起见，这里的名字我起的比较长。但是就像self一样，所有的参数都有它们的传统名称。因此，在真实的产品代码中一个元类应该是像这样的：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">UpperAttrMetaclass</span><span class="params">(type)</span>:</span></div><div class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__new__</span><span class="params">(cls, name, bases, dct)</span>:</span></div><div class="line">        attrs = ((name, value) <span class="keyword">for</span> name, value <span class="keyword">in</span> dct.items() <span class="keyword">if</span> <span class="keyword">not</span> name.startswith(<span class="string">'__'</span>)</div><div class="line">        uppercase_attr  = dict((name.upper(), value) <span class="keyword">for</span> name, value <span class="keyword">in</span> attrs)</div><div class="line">        <span class="keyword">return</span> type.__new__(cls, name, bases, uppercase_attr)</div></pre></td></tr></table></figure>
<p>如果使用super方法的话，我们还可以使它变得更清晰一些，这会缓解继承（是的，你可以拥有元类，从元类继承，从type继承）</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">UpperAttrMetaclass</span><span class="params">(type)</span>:</span></div><div class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__new__</span><span class="params">(cls, name, bases, dct)</span>:</span></div><div class="line">        attrs = ((name, value) <span class="keyword">for</span> name, value <span class="keyword">in</span> dct.items() <span class="keyword">if</span> <span class="keyword">not</span> name.startswith(<span class="string">'__'</span>))</div><div class="line">        uppercase_attr = dict((name.upper(), value) <span class="keyword">for</span> name, value <span class="keyword">in</span> attrs)</div><div class="line">        <span class="keyword">return</span> super(UpperAttrMetaclass, cls).__new__(cls, name, bases, uppercase_attr)</div></pre></td></tr></table></figure>
<p>就是这样，除此之外，关于元类真的没有别的可说的了。使用到元类的代码比较复杂，这背后的原因倒并不是因为元类本身，而是因为你通常会使用元类去做一些晦涩的事情，依赖于自省，控制继承等等。确实，用元类来搞些“黑暗魔法”是特别有用的，因而会搞出些复杂的东西来。但就元类本身而言，它们其实是很简单的：</p>
<p>1)   拦截类的创建</p>
<p>2)   修改类</p>
<p>3)   返回修改之后的类</p>
<h2 id="为什么要用metaclass类而不是函数"><a href="#为什么要用metaclass类而不是函数" class="headerlink" title="为什么要用metaclass类而不是函数?"></a><strong>为什么要用metaclass类而不是函数?</strong></h2><p>由于<strong>metaclass</strong>可以接受任何可调用的对象，那为何还要使用类呢，因为很显然使用类会更加复杂啊？这里有好几个原因：</p>
<p>1）  意图会更加清晰。当你读到UpperAttrMetaclass(type)时，你知道接下来要发生什么。</p>
<p>2） 你可以使用OOP编程。元类可以从元类中继承而来，改写父类的方法。元类甚至还可以使用元类。</p>
<p>3）  你可以把代码组织的更好。当你使用元类的时候肯定不会是像我上面举的这种简单场景，通常都是针对比较复杂的问题。将多个方法归总到一个类中会很有帮助，也会使得代码更容易阅读。</p>
<p>4） 你可以使用<strong>new</strong>, <strong>init</strong>以及<strong>call</strong>这样的特殊方法。它们能帮你处理不同的任务。就算通常你可以把所有的东西都在<strong>new</strong>里处理掉，有些人还是觉得用<strong>init</strong>更舒服些。</p>
<p>5） 哇哦，这东西的名字是metaclass，肯定非善类，我要小心！</p>
<h2 id="究竟为什么要使用元类？"><a href="#究竟为什么要使用元类？" class="headerlink" title="究竟为什么要使用元类？"></a><strong>究竟为什么要使用元类？</strong></h2><p>现在回到我们的大主题上来，究竟是为什么你会去使用这样一种容易出错且晦涩的特性？好吧，一般来说，你根本就用不上它：</p>
<blockquote>
<p>“元类就是深度的魔法，99%的用户应该根本不必为此操心。如果你想搞清楚究竟是否需要用到元类，那么你就不需要它。那些实际用到元类的人都非常清楚地知道他们需要做什么，而且根本不需要解释为什么要用元类。”  —— Python界的领袖 Tim Peters</p>
</blockquote>
<p>元类的主要用途是创建API。一个典型的例子是Django ORM。它允许你像这样定义：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Person</span><span class="params">(models.Model)</span>:</span></div><div class="line">    name = models.CharField(max_length=<span class="number">30</span>)</div><div class="line">    age = models.IntegerField()</div></pre></td></tr></table></figure>
<p>但是如果你像这样做的话：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">guy  = Person(name=<span class="string">'bob'</span>, age=<span class="string">'35'</span>)</div><div class="line"><span class="keyword">print</span> guy.age</div></pre></td></tr></table></figure>
<p>这并不会返回一个IntegerField对象，而是会返回一个int，甚至可以直接从数据库中取出数据。这是有可能的，因为models.Model定义了<strong>metaclass</strong>， 并且使用了一些魔法能够将你刚刚定义的简单的Person类转变成对数据库的一个复杂hook。Django框架将这些看起来很复杂的东西通过暴露出一个简单的使用元类的API将其化简，通过这个API重新创建代码，在背后完成真正的工作。</p>
<h2 id="结语"><a href="#结语" class="headerlink" title="结语"></a><strong>结语</strong></h2><p>首先，你知道了类其实是能够创建出类实例的对象。好吧，事实上，类本身也是实例，当然，它们是元类的实例。</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">&gt;&gt;&gt;<span class="class"><span class="keyword">class</span> <span class="title">Foo</span><span class="params">(object)</span>:</span> <span class="keyword">pass</span></div><div class="line"><span class="meta">&gt;&gt;&gt; </span>id(Foo)</div><div class="line"><span class="number">142630324</span></div></pre></td></tr></table></figure>
<p>Python中的一切都是对象，它们要么是类的实例，要么是元类的实例，除了type。type实际上是它自己的元类，在纯Python环境中这可不是你能够做到的，这是通过在实现层面耍一些小手段做到的。其次，元类是很复杂的。对于非常简单的类，你可能不希望通过使用元类来对类做修改。你可以通过其他两种技术来修改类：</p>
<p>1） <a href="http://en.wikipedia.org/wiki/Monkey_patch" target="_blank" rel="external">Monkey patching</a></p>
<p>2)   class decorators</p>
<p>当你需要动态修改类时，99%的时间里你最好使用上面这两种技术。当然了，其实在99%的时间里你根本就不需要动态修改类 :D</p>

      
    </div>

    <div>
      
        

      
    </div>

    <div>
      
        

      
    </div>


    <footer class="post-footer">
      
        <div class="post-tags">
          
            <a href="/tags/Python/" rel="tag"># Python</a>
          
        </div>
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2017/02/06/HTTP协议详解/" rel="next" title="HTTP协议详解（浏览器信息+服务器信息）">
                <i class="fa fa-chevron-left"></i> HTTP协议详解（浏览器信息+服务器信息）
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2017/02/07/Go语言自制简易WebServer/" rel="prev" title="Go语言自制简易WebServer">
                Go语言自制简易WebServer <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </article>



    <div class="post-spread">
      
    </div>
  </div>

          
          </div>
          


          
  <div class="comments" id="comments">
    
      <div class="ds-thread" data-thread-key="2017/02/06/深刻理解Python中的元类（metaclass）/"
           data-title="深刻理解Python中的元类（metaclass）" data-url="/2017/02/06/深刻理解Python中的元类（metaclass）/">
      </div>
    
  </div>


        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap" >
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
          <img class="site-author-image" itemprop="image"
               src="/images/avatar.gif"
               alt="Porson" />
          <p class="site-author-name" itemprop="name">Porson</p>
          <p class="site-description motion-element" itemprop="description">DBA/Python程序员/开源爱好者</p>
        </div>
        <nav class="site-state motion-element">
        
          
            <div class="site-state-item site-state-posts">
              <a href="/archives">
                <span class="site-state-item-count">14</span>
                <span class="site-state-item-name">日志</span>
              </a>
            </div>
          

          

          
            <div class="site-state-item site-state-tags">
              <a href="/tags">
                <span class="site-state-item-count">10</span>
                <span class="site-state-item-name">标签</span>
              </a>
            </div>
          

        </nav>

        

        <div class="links-of-author motion-element">
          
            
              <span class="links-of-author-item">
                <a href="https://github.com/porson" target="_blank" title="GitHub">
                  
                    <i class="fa fa-fw fa-github"></i>
                  
                  GitHub
                </a>
              </span>
            
          
        </div>

        
        
          <div class="cc-license motion-element" itemprop="license">
            <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" class="cc-opacity" target="_blank">
              <img src="/images/cc-by-nc-sa.svg" alt="Creative Commons" />
            </a>
          </div>
        

        
        

        


      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#类也是对象"><span class="nav-number">1.</span> <span class="nav-text">类也是对象</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#动态地创建类"><span class="nav-number">2.</span> <span class="nav-text">动态地创建类</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#到底什么是元类（终于到主题了）"><span class="nav-number">3.</span> <span class="nav-text">到底什么是元类（终于到主题了）</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#metaclass-属性"><span class="nav-number">4.</span> <span class="nav-text">__metaclass__**属性**</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#自定义元类"><span class="nav-number">5.</span> <span class="nav-text">自定义元类</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#为什么要用metaclass类而不是函数"><span class="nav-number">6.</span> <span class="nav-text">为什么要用metaclass类而不是函数?</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#究竟为什么要使用元类？"><span class="nav-number">7.</span> <span class="nav-text">究竟为什么要使用元类？</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#结语"><span class="nav-number">8.</span> <span class="nav-text">结语</span></a></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright" >
  
  <span class="author" itemprop="copyrightHolder">Create by Porson </span>
    <span class="with-love">
    <i class="fa fa-copyright"></i>
  </span>
   2016 - 
  <span itemprop="copyrightYear">2017</span>
  All copyright

</div>



        

        
      </div>
    </footer>

    <div class="back-to-top">
      <i class="fa fa-arrow-up"></i>
    </div>
  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>









  




  
  <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>

  
  <script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>

  
  <script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>

  
  <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>

  
  <script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>


  


  <script type="text/javascript" src="/js/src/utils.js?v=5.1.0"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=5.1.0"></script>



  
  

  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.0"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=5.1.0"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.0"></script>



  

  
    
  

  <script type="text/javascript">
    var duoshuoQuery = {short_name:"lpx"};
    (function() {
      var ds = document.createElement('script');
      ds.type = 'text/javascript';ds.async = true;
      ds.id = 'duoshuo-script';
      ds.src = (document.location.protocol == 'https:' ? 'https:' : 'http:') + '//static.duoshuo.com/embed.js';
      ds.charset = 'UTF-8';
      (document.getElementsByTagName('head')[0]
      || document.getElementsByTagName('body')[0]).appendChild(ds);
    })();
  </script>

  
    
    
    <script src="/lib/ua-parser-js/dist/ua-parser.min.js?v=0.7.9"></script>
    <script src="/js/src/hook-duoshuo.js"></script>
  












  
  

  
  
    <script type="text/x-mathjax-config">
      MathJax.Hub.Config({
        tex2jax: {
          inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
          processEscapes: true,
          skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
        }
      });
    </script>

    <script type="text/x-mathjax-config">
      MathJax.Hub.Queue(function() {
        var all = MathJax.Hub.getAllJax(), i;
        for (i=0; i < all.length; i += 1) {
          all[i].SourceElement().parentNode.className += ' has-jax';
        }
      });
    </script>
    <script type="text/javascript" src="//cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
  


  

  

  


</body>
</html>
